Categories
JavaScript Best Practices

Better JavaScript — Event Queue

Spread the love

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at ways to improve our JavaScript code.

Don’t Block Event Queue on I/O

JavaScript is a single-threaded language, so blocking I/O with synchronous code is bad if the synchronous code is long-running.

One piece of code can hold up the whole program if we block the event queue.

So we shouldn’t have code like:

const text = downloadSync("http://example.com/file.txt");  
console.log(text);

in our program.

downloadSync waits for the text to be downloaded and then it returns it.

This can take a long time.

Therefore, JavaScript provides us with ways to run code asynchronously.

Instead, we can write:

downloadAsync("http://example.com/file.txt", (text) => {  
  console.log(text);  
});

If we run async JavaScript code, then code is suspended until the result is obtained.

We know the result is obtained when the callback is called.

When the code is suspended, then something queued after this piece of code can run.

We don’t have to worry about some object or variable changing from under us because of concurrently executing code if we structure our code correctly.

Use Callbacks for Asynchronous Code

If we have some simple async code, then we can use callbacks to run our code.

For instance, we had:

downloadAsync("http://example.com/file.txt", (text) => {  
  console.log(text);  
});

that takes a callback that’s called when the result is retrieved from the server.

This is the simplest way to create async code.

When downloadAsync runs, then the function is initiated but hasn’t performed the operation yet.

Once the operation is performed, the callback is run and we get the text .

If we have to run more than one async operation in sequence, then we need to run the 2nd one inside the callback of the first one.

So we can write:

downloadAsync("http://example.com/foo.txt", (text1) => {  
  console.log(text1);  
  downloadAsync("http://example.com/bar.txt", (text2) => {  
    console.log(text2);  
  });  
});

We nest the 2nd downloadAsync call inside the first one.

If there’re more async callbacks we need to run in sequence, then we get more nesting.

This is definitely a problem since we don’t want to nest them.

To avoid this, we can use promises.

Use Promises for Complex Async Operations

If we have lots of async code we need to run in sequence, then the nested async callbacks would be too confusing.

We don’t want to have many levels of nesting in our code since it’s hard to debug and trace.

Instead, we use promises to avoid all the nesting.

For instance, we can create a promise chain by writing:

promise  
  .then((val) => {  
    return promise2;  
  })  
  .then((val) => {  
    return promise3;  
  })  
  .then((val) => {  
    return promise4;  
  })  
  .then((val) => {  
    return promise5;  
  })

We call then with a callback that returns a promise to call another promise.

val has the value that the promise resolved to.

Promises can also be rejected.

To catch errors from rejected promises, we call the catch method:

promise  
  .catch((err) => {  
    console.log(err);  
  })

This is much better than nesting callbacks.

Conclusion

We shouldn’t block I/O of a JavaScript code with long-running synchronous code.

If we have anything that’s potentially long-running, we should use async callbacks or promises.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *